from skimage import feature as ft
from skimage import io
from PIL import Image
import numpy as np
import random

# 方法的参数个数设置不够灵活，后期改进！！！！
class Feature(object):

    def extract_feature(self, img_path, method_name, size, parameters, convert_mode):
        # img = io.imread(img_path, as_grey=if_grey) # 会将灰度除以255
        # img = np.array(Image.open(img_path).convert(convert_mode))
        img = Image.open(img_path).resize(size).convert(convert_mode)
        np_img = np.array(img)
        method = getattr(self, method_name.upper())  # 利用反射性质得到相应方法（还有改进的空间，参数的赋值？？）
        feature = method(np_img, parameters)
        return feature

    # 提取一定比例图片（imgs_path保证为同一类图片，因为这里没有标签的判定）
    # 比例为训练集的比例
    def extract_in_scale(self, imgs_path, method_name, size=(256, 256), parameters = None, ratio = 0.5, convert_mode = 'L', if_shuffle = False, if_numpy = True):
        if if_shuffle:  # 打乱数据集
            random.shuffle(imgs_path)

        trained_imgs = imgs_path[0 : int(len(imgs_path) * ratio)]
        tested_imgs = imgs_path[int(len(imgs_path) * ratio) : len(imgs_path)]

        trained_features = []
        for img_path in trained_imgs:
            feature = self.extract_feature(img_path, method_name, size, parameters, convert_mode)
            trained_features.append(feature)

        tested_features = []
        for img_path in tested_imgs:
            feature = self.extract_feature(img_path, method_name, size, parameters, convert_mode)
            tested_features.append(feature)

        # 转为numpy格式，也可不转
        if if_numpy:
            trained_features = np.asarray(trained_features)
            tested_features = np.asarray(tested_features)
        return trained_features, tested_features

    # 提取指定数目图片（imgs_path保证为同一类图片，因为这里没有标签的判定）
    # 数目为训练集的数目
    def extract_fixed_number(self, imgs_path, method_name, size=(256, 256), parameters = None, selected_num = None, convert_mode = 'L', if_shuffle = False, if_numpy = True):
        if if_shuffle:  # 打乱数据集
            random.shuffle(imgs_path)

        trained_imgs = imgs_path[0 : selected_num]
        tested_imgs = imgs_path[selected_num : len(imgs_path)]

        trained_features = []
        for img_path in trained_imgs:
            feature = self.extract_feature(img_path, method_name, size, parameters, convert_mode)
            trained_features.append(feature)

        tested_features = []
        for img_path in tested_imgs:
            feature = self.extract_feature(img_path, method_name, size, parameters, convert_mode)
            tested_features.append(feature)

        # 转为numpy格式，也可不转
        if if_numpy:
            trained_features = np.asarray(trained_features)
            tested_features = np.asarray(tested_features)
        return trained_features, tested_features

    def GLCM(self, img, parameters):
        if parameters is None:
            raise ValueError('parameters are wrong!')
        distances = parameters[0]
        angles = parameters[1]
        levels = None
        symmetric = False
        normed = False
        if len(parameters) > 2:
            levels = parameters[2]
            symmetric = parameters[3]
            normed = parameters[4]

        matrix = ft.greycomatrix(img, distances, angles, levels = levels, symmetric = symmetric, normed = normed)
        feature = []
        feature.append(ft.greycoprops(matrix, 'contrast')[0, 0])
        feature.append(ft.greycoprops(matrix, 'dissimilarity')[0, 0])
        feature.append(ft.greycoprops(matrix, 'homogeneity')[0, 0])
        feature.append(ft.greycoprops(matrix, 'ASM')[0, 0])
        feature.append(ft.greycoprops(matrix, 'energy')[0, 0])
        feature.append(ft.greycoprops(matrix, 'correlation')[0, 0])
        return feature

    def HOG(self, img, parameters):
        orientations = 9
        pixels_per_cell = (8, 8)
        cells_per_block = (3, 3)
        block_norm = None
        visualize = False
        visualise = None
        transform_sqrt = False
        feature_vector = True
        multichannel = None

        if parameters is not None:
            orientations = parameters[0]
            pixels_per_cell = parameters[1]
            cells_per_block = parameters[2]
            block_norm = parameters[3]
            visualize = parameters[4]
            visualise = parameters[5]
            transform_sqrt = parameters[6]
            feature_vector = parameters[7]
            multichannel = parameters[8]

        feature = ft.hog(img, orientations=orientations, pixels_per_cell=pixels_per_cell, cells_per_block=cells_per_block,
        block_norm=block_norm, visualize = visualize, visualise=visualise, transform_sqrt=transform_sqrt,
        feature_vector=feature_vector, multichannel=multichannel)

        return feature

    def LBP(self, img, parameters):
        if parameters is not None:
            P = parameters[0]
            R = parameters[1]
            if len(parameters) == 2:
                method = 'default'
            elif len(parameters) == 3:
                method = parameters[2]
        else:
            raise ValueError('parameters are wrong!')

        # 从网上看来将LBP图像转为统计直方图，作为特征
        lbp = ft.local_binary_pattern(img, P, R, method=method)
        max_bins = int(lbp.max() + 1)
        feature, _ = np.histogram(lbp, normed=True, bins=max_bins, range=(0, max_bins))

        return feature

    # 保存单个标签的数据特征
    def save_txt(self, features, label_num, filename):
        file = open(filename, 'w+')
        for single_img_features in features:
            file.write("%i " % label_num)
            for index, feature in enumerate(single_img_features):
                context = "%i:" % (index+1) + "%f " % feature
                file.write(context)
            file.write('\n')   # 不知需不需要删除最后一个换行符？？
        file.close()

    # 保存多种标签类型的数据特征
    def save_integrated_txt(self, features, labels, filename):
        file = open(filename, 'w+')
        if len(features) != len(labels):
            raise ValueError("quantity is not equal")
        file_num = len(features)
        for index in range(file_num):
            file.write("%i " % labels[index])
            for index, feature in enumerate(features[index]):
                context = "%i:" % (index + 1) + "%f " % feature
                file.write(context)
            file.write('\n')  # 不知需不需要删除最后一个换行符？？
        file.close()